<?php

/**
 * Maikuro Framework
 *
 * LICENSE:
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * The license is bundled with this package in the file LICENSE.txt.
 * You may also obtain a copy of the License at this URL:
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @copyright  Copyright (c) 2010, Jonathan Hedrén
 * @author     Jonathan Hedrén <jonathan.hedren@gmail.com>
 * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 */

/**
 * @see PDOSelectQuery
 */
require_once 'PDOSelectQuery.php';

/**
 * @see Utility
 */
require_once 'Utility.php';

/**
 * Base class for PDO connections.
 */
class ExtendedPDO extends PDO
{
    /**
     *
     * @param string $dsn
     * @param string $username
     * @param string $passwd
     * @param array $options
     * @return ExtendedPDO 
     */
    public function  __construct($dsn, $username = null, $passwd = null, $options = null) {
        parent::__construct($dsn, $username, $passwd, $options);
        $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $this;
    }

    /**
     *
     * The generated query will look like this:
     * DELETE FROM $tableName [WHERE $whereExpression]
     *
     * @param string $tableName
     * @param string $whereExpression
     * @param string $whereArgs
     * @return DbResult
     */
    public function delete($tableName, $whereExpression = null, $whereArgs = null)
    {
        $sql = 'DELETE FROM ' . $tableName;
        if ($whereExpression != null) {
            $sql .= ' WHERE ' . $whereExpression;
        }
        if ($whereArgs == null || $whereArgs == '') {
            $whereArgs = array();
        } else if (!is_array($whereArgs)) {
            $whereArgs = array($whereArgs);
        }
        
        return $this->executeWriteQuery($sql, $whereArgs);
    }

    /**
     *
     * @param string $sql
     * @param array $values
     * @return integer
     */
    protected function executeWriteQuery($sql, array $values)
    {
        $statement = $this->prepareAndBindValues($sql, $values);

        $statement->execute();

        $rowCount = $statement->rowCount();

        $statement->closeCursor();

        return $rowCount;
    }

    /**
     *
     * The generated SQL will look like this:
     * INSERT INTO $tableName [('column1', 'column2'...)] VALUES ('value1', 'value2', ...)
     *
     * @param string $tableName
     * @param array $values
     * @return integer
     */
    public function insert($tableName, array $values)
    {
        $sql = 'INSERT INTO ' . $tableName;

        if (Utility::isArrayAssociative($values)) {
            $columns = array_keys($values);
            $sql .= ' (' . implode(',', $columns) . ')';
        }
        $sql .= ' VALUES (' . implode(',', array_fill(0, count($values), '?')) . ')';

        return $this->executeWriteQuery($sql, $values);
    }

    /**
     * Prepares a statement by binding the values to it.
     *
     * @param string $sql
     * @param array $values
     * @return PDOStatement
     */
    public function prepareAndBindValues($sql, array $values)
    {
        $statement = $this->prepare($sql);

        if (count($values)) {
            $i = 1;
            foreach ($values as $value) {
                if (is_integer($value)) {
                    $dataType = PDO::PARAM_INT;
                } else if ($value === null){
                    $dataType = PDO::PARAM_NULL;
                } else {
                    $dataType = PDO::PARAM_STR;
                }
                $statement->bindValue($i++, $value, $dataType);
            }
        }
        return $statement;
    }

    /**
     *
     * @param array|string $columns
     * @return PDOSelectQuery
     */
    public function select($columns = null)
    {
        $selectQuery = new PDOSelectQuery($this);
        if ($columns !== null) {
            if (!is_array($columns)) {
                $columns = func_get_args();
            }
            $selectQuery->columns($columns);
        }
        return $selectQuery;
    }

    /**
     *
     * The generated SQL will look like this:
     * UPDATE $tableName SET column1 = ?, column2 = ?, ...
     *
     * @param string $tableName
     * @param array $fieldsAndValues
     * @param string $whereExpression
     * @param mixed $whereArgs
     * @return integer
     */
    public function update($tableName, array $fieldsAndValues, $whereExpression = null, $whereArgs = null)
    {
        $fields = '';
        foreach (array_keys($fieldsAndValues) as $field) {
            $fields .= $field . ' = ?, ';
        }
        $values = array_values($fieldsAndValues);
        $sql = 'UPDATE ' . $tableName . ' SET ' . trim($fields, ' ,');

        if ($whereExpression !== null && $whereArgs !== null) {
            $sql .= ' WHERE ' . $whereExpression;
            if (is_array($whereArgs)) {
                $values = $values + $whereArgs;
            } else {
                $values[] = $whereArgs;
            }
        }

        return $this->executeWriteQuery($sql, $values);
    }
}
